package org.finesys.common.netty.websocket.resolver;

import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.finesys.common.netty.websocket.annotation.RequestParam;
import org.finesys.common.netty.websocket.support.MethodArgumentResolver;
import org.springframework.core.MethodParameter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;

import static org.finesys.common.netty.websocket.constants.WebSocketAttributeKeyConstants.REQUEST_PARAM;


public class RequestParamMapMethodArgumentResolver implements MethodArgumentResolver {

    /**
     * 重写support方法以判断是否支持给定的方法参数。
     *
     * 如果方法参数上标注了@RequestParam注解，并且方法参数的类型是Map或其子类，
     * 同时@RequestParam注解的value属性为空，则返回true，表示支持该参数；
     * 否则返回false。
     *
     * @param parameter 方法参数，包含方法参数的元数据。
     * @return 如果支持给定的方法参数，则返回true；否则返回false。
     */
    @Override
    public boolean support(MethodParameter parameter) {
        RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
        return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType())) && !StringUtils.hasText(requestParam.value());
    }

    /**
     * 重写resolveArgument方法以解析方法参数。
     *
     * 此方法用于从WebSocket通道接收到的请求中解析出方法参数。它首先检查方法参数上是否标注了@RequestParam注解，
     * 然后从请求参数中获取对应的值。如果@RequestParam注解不存在，将抛出IllegalArgumentException异常。
     *
     * 如果@RequestParam注解中的value属性为空，则尝试从方法参数名中获取参数名。如果方法参数名也不存在，
     * 同样会抛出IllegalArgumentException异常。
     *
     * 如果通道尚未存储请求参数，则使用QueryStringDecoder解析请求URI中的查询参数，并将其存储在通道属性中。
     *
     * 接下来，将请求参数封装到一个MultiValueMap中，以便更方便地处理多个值对应一个键的情况。
     *
     * 根据方法参数的类型，选择返回MultiValueMap或将其转换为SingleValueMap。如果方法参数类型为MultiValueMap，
     * 则直接返回封装好的MultiValueMap；否则，将MultiValueMap转换为SingleValueMap并返回。
     *
     * @param parameter 方法参数，包含方法参数的元数据。
     * @param channel   WebSocket通道，用于处理WebSocket连接。
     * @param object    从WebSocket通道接收到的对象，预期为FullHttpRequest类型。
     * @return 解析并转换后的方法参数值，可能为MultiValueMap或SingleValueMap类型。
     * @throws Exception 如果在解析或转换参数过程中发生异常，则抛出该异常。
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, Channel channel, Object object) throws Exception {
        RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
        if (requestParam == null) {
            throw new IllegalArgumentException(
                    "Name for argument type [" + parameter.getNestedParameterType().getName()
                            + "] not found @RequestParam annotation");
        }
        String name = requestParam.value();
        if (!StringUtils.hasText(name)) {
            name = parameter.getParameterName();
            if (name == null) {
                throw new IllegalArgumentException(
                        "Name for argument type [" + parameter.getNestedParameterType().getName()
                                + "] not available, and parameter name information not found in class file either.");
            }
        }
        if (!channel.hasAttr(REQUEST_PARAM)) {
            QueryStringDecoder decoder = new QueryStringDecoder(((FullHttpRequest) object).uri());
            channel.attr(REQUEST_PARAM).set(decoder.parameters());
        }
        Map<String, List<String>> requestParams = channel.attr(REQUEST_PARAM).get();
        MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>(requestParams);
        if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
            return multiValueMap;
        } else {
            return multiValueMap.toSingleValueMap();
        }
    }
}
